Panduan komprehensif bagi developer dan insinyur keamanan tentang cara mengaudit kode TypeScript untuk kerentanan umum seperti XSS, SQLi, dan lainnya menggunakan SAST, DAST, dan SCA.
Audit Keamanan TypeScript: Penyelaman Mendalam ke Deteksi Jenis Kerentanan
TypeScript telah menggemparkan dunia pengembangan, menawarkan kekokohan pengetikan statis di atas fleksibilitas JavaScript. Ini memberdayakan segalanya mulai dari aplikasi frontend yang kompleks dengan kerangka kerja seperti Angular dan React hingga layanan backend berkinerja tinggi dengan Node.js. Meskipun kompiler TypeScript luar biasa dalam menangkap kesalahan terkait tipe dan meningkatkan kualitas kode, sangat penting untuk memahami kebenaran mendasar: TypeScript bukanlah solusi pamungkas untuk keamanan.
Keamanan tipe mencegah kelas bug tertentu, seperti pengecualian null pointer atau tipe data yang salah yang diteruskan ke fungsi. Namun, itu tidak secara inheren mencegah cacat keamanan logis. Kerentanan seperti Cross-Site Scripting (XSS), SQL Injection (SQLi), dan Broken Access Control berakar pada logika aplikasi dan penanganan data, area yang berada di luar lingkup langsung pemeriksa tipe. Di sinilah audit keamanan menjadi sangat diperlukan.
Panduan komprehensif ini dirancang untuk audiens global yang terdiri dari pengembang, profesional keamanan, dan pemimpin rekayasa. Kami akan menjelajahi lanskap keamanan TypeScript, mempelajari jenis kerentanan yang paling umum, dan memberikan strategi yang dapat ditindaklanjuti untuk mendeteksi dan menanggulanginya menggunakan kombinasi analisis statis (SAST), analisis dinamis (DAST), dan analisis komposisi perangkat lunak (SCA).
Memahami Lanskap Keamanan TypeScript
Sebelum menyelami teknik deteksi spesifik, penting untuk membingkai konteks keamanan untuk aplikasi TypeScript pada umumnya. Aplikasi modern adalah sistem kompleks dari kode pihak pertama, pustaka pihak ketiga, dan konfigurasi infrastruktur. Kerentanan di salah satu lapisan ini dapat membahayakan seluruh sistem.
Mengapa Keamanan Tipe Saja Tidak Cukup
Perhatikan cuplikan kode Express.js sederhana dalam TypeScript ini:
import express from 'express';
import { db } from './database';
const app = express();
app.get('/user', async (req, res) => {
const userId: string = req.query.id as string;
// Tipenya benar, tetapi logikanya cacat!
const query = `SELECT * FROM users WHERE id = '${userId}'`;
const user = await db.query(query);
res.json(user);
});
Dari perspektif kompiler TypeScript, kode ini valid sempurna. `userId` diketik dengan benar sebagai `string`. Namun, dari sudut pandang keamanan, ini berisi kerentanan SQL Injection klasik. Seorang penyerang dapat memberikan `userId` seperti ' OR 1=1; -- untuk melewati otentikasi dan mengambil semua pengguna dari basis data. Ini menggambarkan celah yang harus diisi oleh audit keamanan: menganalisis alur dan penanganan data, bukan hanya tipenya.
Vektor Serangan Umum pada Aplikasi TypeScript
Sebagian besar kerentanan yang ditemukan di aplikasi JavaScript juga sama lazimnya di TypeScript. Saat mengaudit, ada baiknya membingkai pencarian Anda di sekitar kategori yang sudah mapan, seperti yang berasal dari OWASP Top 10:
- Injeksi (Injection): SQLi, NoSQLi, Injeksi Perintah, dan Injeksi Log di mana data yang tidak tepercaya dikirim ke interpreter sebagai bagian dari perintah atau kueri.
- Cross-Site Scripting (XSS): XSS tersimpan, terpantul, dan berbasis DOM di mana data yang tidak tepercaya disertakan dalam halaman web tanpa 'escaping' yang tepat.
- Deserialisasi Tidak Aman (Insecure Deserialization): Melakukan deserialisasi data yang tidak tepercaya dapat menyebabkan eksekusi kode jarak jauh (RCE) jika logika aplikasi dapat dimanipulasi.
- Kontrol Akses yang Rusak (Broken Access Control): Celah dalam penegakan izin, memungkinkan pengguna mengakses data atau melakukan tindakan yang seharusnya tidak mereka lakukan.
- Paparan Data Sensitif (Sensitive Data Exposure): Rahasia yang ditulis secara permanen (kunci API, kata sandi), kriptografi yang lemah, atau mengekspos data sensitif dalam log atau pesan kesalahan.
- Menggunakan Komponen dengan Kerentanan yang Diketahui: Bergantung pada paket `npm` pihak ketiga dengan cacat keamanan yang terdokumentasi.
Pengujian Keamanan Analisis Statis (SAST) dalam TypeScript
Pengujian Keamanan Analisis Statis, atau SAST, melibatkan analisis kode sumber aplikasi untuk kerentanan keamanan tanpa menjalankannya. Untuk bahasa yang dikompilasi seperti TypeScript, ini adalah pendekatan yang sangat kuat karena kita dapat memanfaatkan infrastruktur kompiler.
Kekuatan Abstract Syntax Tree (AST) TypeScript
Ketika kompiler TypeScript memproses kode Anda, pertama-tama ia membuat Abstract Syntax Tree (AST). AST adalah representasi pohon dari struktur kode. Setiap node di pohon mewakili sebuah konstruk, seperti deklarasi variabel, pemanggilan fungsi, atau ekspresi biner. Dengan melintasi pohon ini secara terprogram, alat SAST dapat memahami logika kode dan, yang lebih penting, melacak alur data.
Ini memungkinkan kita untuk melakukan analisis noda (taint analysis): mengidentifikasi di mana input pengguna yang tidak tepercaya ("sumber") mengalir melalui aplikasi dan mencapai fungsi yang berpotensi berbahaya ("tujuan") tanpa sanitasi atau validasi yang tepat.
Mendeteksi Pola Kerentanan dengan SAST
Celah Injeksi (SQLi, NoSQLi, Injeksi Perintah)
- Pola: Cari input yang dikontrol pengguna yang secara langsung digabungkan atau diinterpolasi ke dalam string yang kemudian dieksekusi oleh driver basis data, shell, atau interpreter lainnya.
- Sumber (Asal Noda): `req.body`, `req.query`, `req.params` di Express/Koa, `process.argv`, pembacaan file.
- Tujuan (Fungsi Berbahaya): `db.query()`, `Model.find()`, `child_process.exec()`, `eval()`.
- Contoh Rentan (SQLi):
// SUMBER: req.query.category adalah input pengguna yang tidak tepercaya const category: string = req.query.category as string; // TUJUAN: Variabel category mengalir ke dalam kueri basis data tanpa sanitasi const products = await db.query(`SELECT * FROM products WHERE category = '${category}'`); - Strategi Deteksi: Alat SAST akan melacak variabel `category` dari sumbernya (`req.query`) ke tujuannya (`db.query`). Jika mendeteksi bahwa variabel tersebut adalah bagian dari templat string yang diteruskan ke tujuan, itu akan menandai potensi kerentanan injeksi. Perbaikannya adalah menggunakan kueri berparameter, di mana driver basis data menangani 'escaping' dengan benar.
Cross-Site Scripting (XSS)
- Pola: Data yang tidak tepercaya dirender ke dalam DOM tanpa di-'escape' dengan benar untuk konteks HTML.
- Sumber: Setiap data yang diberikan pengguna dari API, formulir, atau parameter URL.
- Tujuan: `element.innerHTML`, `document.write()`, `dangerouslySetInnerHTML` milik React, `v-html` milik Vue.
- Contoh Rentan (React):
function UserComment({ commentText }: { commentText: string }) { // SUMBER: commentText berasal dari sumber eksternal // TUJUAN: dangerouslySetInnerHTML menulis HTML mentah ke DOM return ; } - Strategi Deteksi: Proses audit melibatkan identifikasi semua penggunaan tujuan manipulasi DOM yang tidak aman ini. Alat tersebut kemudian melakukan analisis aliran data mundur untuk melihat apakah data berasal dari sumber yang tidak tepercaya. Kerangka kerja frontend modern seperti React dan Angular menyediakan 'auto-escaping' secara default, jadi fokus utama harus pada penggantian yang disengaja seperti yang ditunjukkan di atas.
Deserialisasi Tidak Aman
- Pola: Aplikasi menggunakan sebuah fungsi untuk melakukan deserialisasi data dari sumber yang tidak tepercaya, yang berpotensi membuat instance kelas arbitrer atau mengeksekusi kode.
- Sumber: Cookie yang dikontrol pengguna, payload API, atau data yang dibaca dari file.
- Tujuan: Fungsi dari pustaka yang tidak aman seperti `node-serialize`, `serialize-javascript` (dalam konfigurasi tertentu), atau logika deserialisasi kustom.
- Contoh Rentan:
import serialize from 'node-serialize'; app.post('/profile', (req, res) => { // SUMBER: req.body.data sepenuhnya dikontrol oleh pengguna const userData = Buffer.from(req.body.data, 'base64').toString(); // TUJUAN: Deserialisasi yang tidak aman dapat menyebabkan RCE const obj = serialize.unserialize(userData); // ... proses obj }); - Strategi Deteksi: Alat SAST memelihara daftar fungsi deserialisasi yang diketahui tidak aman. Mereka memindai basis kode untuk setiap panggilan ke fungsi-fungsi ini dan menandainya. Mitigasi utama adalah menghindari deserialisasi data yang tidak tepercaya atau menggunakan format aman yang hanya berisi data seperti JSON dengan `JSON.parse()`.
Pengujian Keamanan Analisis Dinamis (DAST) untuk Aplikasi TypeScript
Sementara SAST menganalisis kode dari dalam ke luar, Pengujian Keamanan Analisis Dinamis (DAST) bekerja dari luar ke dalam. Alat DAST berinteraksi dengan aplikasi yang sedang berjalan—biasanya di lingkungan pementasan atau pengujian—dan menyelidikinya untuk mencari kerentanan seperti yang dilakukan penyerang sungguhan. Mereka tidak memiliki pengetahuan tentang kode sumber.
Mengapa DAST Melengkapi SAST
DAST sangat penting karena dapat mengungkap masalah yang mungkin terlewat oleh SAST, seperti:
- Masalah Lingkungan dan Konfigurasi: Server yang salah konfigurasi, header keamanan HTTP yang salah, atau titik akhir administratif yang terbuka.
- Kerentanan Waktu Jalan (Runtime): Celah yang hanya muncul saat aplikasi berjalan dan berinteraksi dengan layanan lain, seperti basis data atau lapisan caching.
- Celah Logika Bisnis yang Kompleks: Masalah dalam proses multi-langkah (mis., alur pembayaran) yang sulit dimodelkan hanya dengan analisis statis.
Teknik DAST untuk API dan Aplikasi Web TypeScript
Fuzzing Endpoint API
Fuzzing melibatkan pengiriman volume tinggi data yang tidak terduga, salah format, atau acak ke titik akhir API untuk melihat bagaimana aplikasi merespons. Untuk backend TypeScript, ini bisa berarti:
- Mengirim objek JSON yang sangat bersarang ke titik akhir POST untuk menguji injeksi NoSQL atau kelelahan sumber daya.
- Mengirim string di mana angka diharapkan, atau integer di mana boolean diharapkan, untuk mengungkap penanganan kesalahan yang buruk yang mungkin membocorkan informasi.
- Menyuntikkan karakter khusus (`'`, `"`, `<`, `>`) ke semua parameter untuk menyelidiki celah injeksi dan XSS.
Mensimulasikan Serangan Dunia Nyata
Pemindai DAST akan memiliki pustaka payload serangan yang diketahui. Ketika menemukan bidang input atau parameter API, ia akan secara sistematis menyuntikkan payload ini dan menganalisis respons aplikasi.
- Untuk SQLi: Alat ini mungkin mengirim payload seperti `1' UNION SELECT username, password FROM users--`. Jika respons berisi data sensitif, titik akhir tersebut rentan.
- Untuk XSS: Alat ini mungkin mengirim ``. Jika HTML respons berisi string yang sama persis dan tidak di-'escape', ini menunjukkan kerentanan XSS terpantul.
Menggabungkan SAST, DAST, dan SCA untuk Cakupan Komprehensif
Baik SAST maupun DAST saja tidak cukup. Strategi audit keamanan yang matang mengintegrasikan keduanya, bersama dengan komponen ketiga yang krusial: Analisis Komposisi Perangkat Lunak (SCA).
Analisis Komposisi Perangkat Lunak (SCA): Masalah Rantai Pasokan
Ekosistem Node.js, yang menopang sebagian besar pengembangan backend TypeScript, sangat bergantung pada paket sumber terbuka dari registri `npm`. Satu proyek dapat memiliki ratusan atau bahkan ribuan dependensi langsung dan transitif. Kerentanan di salah satu paket ini adalah kerentanan di aplikasi Anda.
Alat SCA bekerja dengan memindai file manifes dependensi Anda (`package.json` dan `package-lock.json` atau `yarn.lock`). Mereka membandingkan versi paket yang Anda gunakan dengan basis data global kerentanan yang diketahui (seperti GitHub Advisory Database).
Alat SCA Esensial:
- `npm audit` / `yarn audit`: Perintah bawaan yang menyediakan cara cepat untuk memeriksa dependensi yang rentan.
- GitHub Dependabot: Secara otomatis memindai repositori dan membuat pull request untuk memperbarui dependensi yang rentan.
- Snyk Open Source: Alat komersial populer yang menawarkan informasi kerentanan terperinci dan saran perbaikan.
Menerapkan Model Keamanan "Shift Left"
"Menggeser ke kiri" (Shifting left) berarti mengintegrasikan praktik keamanan sedini mungkin ke dalam siklus hidup pengembangan perangkat lunak (SDLC). Tujuannya adalah untuk menemukan dan memperbaiki kerentanan saat biaya dan usahanya paling sedikit—selama pengembangan.
Pipeline CI/CD modern yang aman untuk proyek TypeScript seharusnya terlihat seperti ini:
- Mesin Developer: Plugin IDE dan pre-commit hook menjalankan linter dan pemindaian SAST ringan.
- Saat Commit/Pull Request: Server CI memicu pemindaian SAST komprehensif dan pemindaian SCA. Jika kerentanan kritis ditemukan, build akan gagal.
- Saat Merge ke Staging: Aplikasi di-deploy ke lingkungan staging. Server CI kemudian memicu pemindaian DAST terhadap lingkungan yang sedang berjalan ini.
- Saat Deployment ke Produksi: Setelah semua pemeriksaan lolos, kode di-deploy. Alat pemantauan berkelanjutan dan perlindungan runtime mengambil alih.
Peralatan dan Implementasi Praktis
Teori itu penting, tetapi implementasi praktis adalah kuncinya. Berikut adalah beberapa alat dan teknik untuk diintegrasikan ke dalam alur kerja pengembangan TypeScript Anda.
Plugin ESLint Esensial untuk Keamanan
ESLint adalah linter yang kuat dan dapat dikonfigurasi untuk JavaScript dan TypeScript. Anda dapat menggunakannya sebagai alat SAST ringan yang berfokus pada pengembang dengan menambahkan plugin khusus keamanan:
- `eslint-plugin-security`: Menangkap jebakan keamanan umum Node.js seperti menggunakan `child_process.exec()` dengan variabel yang tidak di-'escape' atau mendeteksi pola regex yang tidak aman yang dapat menyebabkan Denial of Service (DoS).
- `eslint-plugin-no-unsanitized`: Menyediakan aturan untuk membantu mencegah XSS dengan menandai penggunaan `innerHTML`, `outerHTML`, dan properti berbahaya lainnya.
- Aturan Kustom: Untuk kebijakan keamanan spesifik organisasi, Anda dapat menulis aturan ESLint Anda sendiri. Misalnya, Anda bisa menulis aturan yang melarang mengimpor pustaka kriptografi internal yang sudah usang.
Contoh Integrasi Pipeline CI/CD (GitHub Actions)
Berikut adalah contoh sederhana alur kerja GitHub Actions yang menggabungkan SCA dan SAST:
name: TypeScript Security Scan
on: [pull_request]
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run dependency audit (SCA)
# --audit-level=high menggagalkan build untuk kerentanan tingkat keparahan tinggi
run: npm audit --audit-level=high
- name: Run security linter (SAST)
run: npx eslint . --ext .ts --quiet
# Contoh integrasi pemindai SAST yang lebih canggih seperti CodeQL
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: typescript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
Di Luar Kode: Keamanan Runtime dan Arsitektural
Audit yang komprehensif juga mempertimbangkan arsitektur dan lingkungan runtime yang lebih luas.
API yang Type-Safe
Salah satu cara terbaik untuk mencegah seluruh kelas bug antara frontend dan backend Anda adalah dengan memberlakukan keamanan tipe di seluruh batas API. Alat seperti tRPC, GraphQL dengan pembuatan kode (misalnya, GraphQL Code Generator), atau generator OpenAPI memungkinkan Anda berbagi tipe antara klien dan server. Jika Anda mengubah tipe respons API backend, kode frontend TypeScript Anda akan gagal dikompilasi, mencegah kesalahan runtime dan potensi masalah keamanan dari kontrak data yang tidak konsisten.
Praktik Terbaik Node.js
Karena banyak aplikasi TypeScript berjalan di Node.js, sangat penting untuk mengikuti praktik terbaik khusus platform:
- Gunakan Header Keamanan: Gunakan pustaka seperti `helmet` untuk Express untuk mengatur header HTTP penting (seperti `Content-Security-Policy`, `X-Content-Type-Options`, dll.) yang membantu mengurangi XSS dan serangan sisi klien lainnya.
- Jalankan dengan Hak Istimewa Terendah: Jangan menjalankan proses Node.js Anda sebagai pengguna root, terutama di dalam kontainer.
- Selalu Perbarui Runtime: Perbarui versi Node.js dan TypeScript Anda secara teratur untuk menerima patch keamanan.
Kesimpulan dan Poin-Poin yang Dapat Ditindaklanjuti
TypeScript menyediakan fondasi yang fantastis untuk membangun aplikasi yang andal dan dapat dipelihara. Namun, keamanan adalah praktik yang terpisah dan disengaja. Ini membutuhkan strategi pertahanan berlapis yang menggabungkan analisis kode statis, pengujian runtime dinamis, dan manajemen rantai pasokan yang waspada.
Dengan memahami jenis kerentanan umum dan mengintegrasikan alat dan proses yang tepat ke dalam siklus hidup pengembangan Anda, Anda dapat secara signifikan meningkatkan postur keamanan aplikasi TypeScript Anda.
Langkah-Langkah yang Dapat Ditindaklanjuti untuk Developer
- Aktifkan Mode Ketat (Strict Mode): Di `tsconfig.json` Anda, atur `"strict": true`. Ini mengaktifkan serangkaian perilaku pemeriksaan tipe yang mencegah kesalahan umum.
- Periksa Kode Anda dengan Lint: Tambahkan `eslint-plugin-security` ke proyek Anda dan perbaiki masalah yang dilaporkannya.
- Audit Dependensi Anda: Jalankan `npm audit` atau `yarn audit` secara teratur dan jaga agar dependensi Anda tetap terbaru.
- Jangan Pernah Mempercayai Input Pengguna: Perlakukan semua data yang datang dari luar aplikasi Anda sebagai berpotensi berbahaya. Selalu validasi, sanitasi, atau 'escape' dengan tepat untuk konteks di mana data itu akan digunakan.
Langkah-Langkah yang Dapat Ditindaklanjuti untuk Tim dan Organisasi
- Otomatiskan Keamanan di CI/CD: Integrasikan pemindaian SAST, DAST, dan SCA langsung ke dalam pipeline build dan deployment Anda. Gagalkan build jika ditemukan temuan kritis.
- Kembangkan Budaya Keamanan: Berikan pelatihan rutin tentang praktik pengkodean yang aman. Dorong pengembang untuk berpikir secara defensif.
- Lakukan Audit Manual: Untuk aplikasi penting, lengkapi peralatan otomatis dengan tinjauan kode manual berkala dan pengujian penetrasi oleh para ahli keamanan.
Keamanan bukanlah fitur yang ditambahkan di akhir proyek; ini adalah proses yang berkelanjutan. Dengan mengadopsi pendekatan proaktif dan berlapis untuk audit, Anda dapat memanfaatkan kekuatan penuh TypeScript sambil membangun perangkat lunak yang lebih aman dan tangguh untuk basis pengguna global.